#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
#include <opencv2/opencv.hpp>
#include <assert.h>
#include "NvInfer.h"
#include "NvOnnxConfig.h"
#include "NvOnnxParser.h"
#include "NvInferRuntime.h"
#include "cuda_runtime.h"
#include "preprocess.h"
#include <yaml-cpp/yaml.h>



void PreProcess:: print_GPU_mem(int line)
{
    size_t free_byte;
    size_t total_byte;


    cudaError_t cudaSuccess = cudaMemGetInfo(&free_byte, &total_byte);
    if (cudaSuccess != cudaSuccess) 
    {
        std::cerr << "CUDA Error: Unable to retrieve memory info." << std::endl;
    }
    else
    {
        double free_mb = free_byte / 1e6;
        double total_mb = total_byte / 1e6;
        std::cout<<"__LINE__: "<<line<<std::endl;
        // auto debug_logger_buff = debug_logger_dict123["123"];
        // debug_logger_buff->info("__LINE__: "+to_string(line));
        std::cout << "Free memory: " << free_mb << " MB" << std::endl;
        // debug_logger_buff->info("Free memory: " +to_string(free_mb)+ " MB");
        // debug_logger_buff->info("***********************************************");
        // debug_logger_buff->flush();
        std::cout << "Total memory: " << total_mb << " MB" << std::endl;
    }
}


PreProcess::PreProcess(){}

PreProcess::~PreProcess()
{
}

// int PreProcess::alloc_resource(){
//     ori_image_size = params_.old_h * params_.old_w * 3 * sizeof(float);
//     target_image_size = params_.height * params_.width * 3 * sizeof(float);
//     checkCudaErrors(cudaMallocManaged((void **)&left, ori_image_size));
//     checkCudaErrors(cudaMallocManaged((void **)&voxels_temp_, voxels_temp_size_));

//     voxel_num_size_ = params_.max_voxels * sizeof(unsigned int);
//     voxel_idxs_size_ = params_.max_voxels * 4 * sizeof(unsigned int);

//     checkCudaErrors(cudaMallocManaged((void **)&d_voxel_features_, voxel_features_size_));
//     checkCudaErrors(cudaMallocManaged((void **)&d_voxel_num_, voxel_num_size_));
//     checkCudaErrors(cudaMallocManaged((void **)&d_voxel_indices_, voxel_idxs_size_));
//     checkCudaErrors(cudaMalloc((void **)&d_real_num_voxels_, sizeof(unsigned int)));
//     checkCudaErrors(cudaMallocHost((void **)&h_real_num_voxels_, sizeof(unsigned int)));
    
//     checkCudaErrors(cudaMemset(d_voxel_num_, 0, voxel_num_size_));
//     checkCudaErrors(cudaMemset(d_voxel_features_, 0, voxel_features_size_));
//     checkCudaErrors(cudaMemset(d_voxel_indices_, 0, voxel_idxs_size_));
//     checkCudaErrors(cudaMemset(d_real_num_voxels_, 0, sizeof(unsigned int)));

//     return 0;
// }

// template <typename T>
int PreProcess::Random_crop(unsigned char*output_ptr, cv::Mat ori_image, const float REL_X, const float REL_Y,
                const int old_h, const int old_w, const int MAX_CROP_H_LIMIT, 
                const int MAX_CROP_H, const int MAX_CROP_W, cudaStream_t stream){

                    unsigned char* output_image_gpu;
                    unsigned char* input_image_gpu;
                    // allocate cuda memory
                    // int device = 0; // 设备编号，如果有多个GPU可以选择其他编号
                    // cudaDeviceProp deviceProp;
                    // cudaGetDeviceProperties(&deviceProp, device);
                    // std::cout << "Device Name: " << deviceProp.name << std::endl;
                    // std::cout << "Total Global Memory: " << deviceProp.totalGlobalMem << " bytes" << std::endl;
                    // std::cout << "REL_Y" << REL_Y << std::endl;
                    print_GPU_mem(399);
                    // std::cout << "target h: " << MAX_CROP_H << "target w: " << MAX_CROP_W << std::endl;
                    cudaMalloc((void **)&input_image_gpu, sizeof(ori_image.elemSize())*old_h * old_w * 1);
                    cudaMalloc((void **)&output_image_gpu, sizeof(ori_image.elemSize())*MAX_CROP_H * MAX_CROP_W * 1);
                    cudaMemcpy(input_image_gpu, ori_image.data, sizeof(ori_image.elemSize())*old_h * old_w * 1, cudaMemcpyHostToDevice);
                    // std::cout << ori_image.total() << std::endl;
                    
                    int crop_rel_x = REL_X / 2 + 0.5;
                    int crop_rel_y = REL_Y / 2 + 0.5;
                    int crop_h = std::min(MAX_CROP_H, old_h);
                    int crop_w = std::min(MAX_CROP_W, old_w);
                    if (!(crop_h <= old_h && crop_w <= old_w && 0 <= crop_rel_x && crop_rel_x <= 1 && 0 <= crop_rel_y && crop_rel_y <=1)){
                        std::cerr << "Error: Conditions are not met." << std::endl;
                        exit(EXIT_FAILURE);
                    }
                    std::cout << "crop_h: " << crop_h << "crop_w: " << crop_w << std::endl;
                    std::cout << "crop_rel_x: " << crop_rel_x << "crop_rel_y: " << crop_rel_y << std::endl;
                    int x1 = (old_w - crop_w) * crop_rel_x;
                    int y1 = (old_h - crop_h) * crop_rel_y;
                    // int x1 = 0;
                    // int y1 = 0;
                    

                    // launch cuda kernel function
                    std::cout << "x1 : " << x1 << "y1: " << y1 << std::endl;
                    print_GPU_mem(44);
                    checkCudaErrors(RandomCrop_launch(input_image_gpu, output_image_gpu,
                                                old_w, old_h, MAX_CROP_H, MAX_CROP_W, x1, y1, 3, stream));
                    print_GPU_mem(45);

                    //cpu mat
                    // std::cout << output_ptr << std::endl;
                    
                    checkCudaErrors(cudaMemcpy(output_ptr, output_image_gpu, sizeof(ori_image.elemSize()) * MAX_CROP_H * MAX_CROP_W * 1, cudaMemcpyDeviceToHost));
                    print_GPU_mem(116);
                    cudaFree(input_image_gpu);
                    print_GPU_mem(118);
                    cudaFree(output_image_gpu);
                    print_GPU_mem(120);
                    // cudaDeviceReset();
                    // print_GPU_mem(121);
                    return 0;
                }
                
// int generate_depth_intervals(YAML::Node config, )
// calibration get_calibration(std::string txt_file){
//     calibration calib(std::string txt_file);
    
// }

// 读取图片数据
int read_image(std::string &image_names, std::vector<char> &raw_data){

    std::ifstream input(image_names.c_str(), std::ios::in | std::ios::binary | std::ios::ate);

    if (!(input.is_open())){
        std::cerr << "Cannot open image: " << image_names << std::endl;
        return EXIT_FAILURE;
    }

    std::streamsize file_size = input.tellg();
    input.seekg(0, std::ios::beg);
    if (raw_data.size() < (size_t)file_size){
        raw_data.resize(file_size);
    }
    if (!input.read(raw_data.data(), file_size)){
        std::cerr << "Cannot read from file: " << image_names << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

int read_sample(std::vector<std::string> &imgs_file, std::vector<std::vector<char>> &imgs_data){

    imgs_data.resize(imgs_file.size());

    for(size_t i = 0; i < imgs_data.size(); i++){
        if(read_image(imgs_file[i], imgs_data[i])){
            return EXIT_FAILURE;
        }
    }
    return EXIT_SUCCESS;
}

static std::vector<unsigned char*> load_images(const std::string& root) {
  const char* file_names[] = {"0-FRONT.jpg", "1-FRONT_RIGHT.jpg", "2-FRONT_LEFT.jpg",
                              "3-BACK.jpg",  "4-BACK_LEFT.jpg",   "5-BACK_RIGHT.jpg"};

  std::vector<unsigned char*> images;
  for (int i = 0; i < 6; ++i) {
    char path[200];
    sprintf(path, "%s/%s", root.c_str(), file_names[i]);

    int width, height, channels;
    images.push_back(stbi_load(path, &width, &height, &channels, 0));
    // printf("Image info[%d]: %d x %d : %d\n", i, width, height, channels);
  }
  return images;
}